package com.gitee.magic.context;

import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import com.gitee.magic.context.converter.ListConverterEditor;
import com.gitee.magic.context.converter.MapConverterEditor;
import com.gitee.magic.context.converter.ObjectConverterEditorExt;
import com.gitee.magic.context.converter.SetConverterEditor;
import com.gitee.magic.core.converter.AbstractConverterEditor;
import com.gitee.magic.core.converter.PropertyConverter;
import com.gitee.magic.core.json.JsonArray;
import com.gitee.magic.core.utils.reflect.TypeReference;

/**
 * @author start
 */
public class ConverterEditorUtils {

	public static <T, R> JsonArray converter(List<T> targets) {
		JsonArray json = new JsonArray();
		for (T target : targets) {
			json.put(converterObject(target));
		}
		return json;
	}

	@SuppressWarnings("unchecked")
	public static <T,R> R converter(T target,Class<R> prototype) {
		return (R)converterObject(target);
	}
	
	public static <T> Object converterObject(T target) {
		AbstractConverterEditor<?> editor = getBasicConverter(target.getClass());
		editor.setValue(target);
		return editor.getSource();
	}

	/////////////////////////////////////

	public static <T> List<T> restoreList(Class<T> target, JsonArray value) {
		return restoreList(target, value, new TypeReference<T>() {
		});
	}

	public static <T, R> List<R> restoreList(Class<T> target, JsonArray value, TypeReference<R> references) {
		List<R> list = new ArrayList<>();
		for (int i = 0; i < value.length(); i++) {
			list.add(restoreObject(target, value.get(i), references));
//			list.add(restoreObject(target, value.getJSONObject(i), references));
		}
		return list;
	}

	public static <T> T restoreObject(Class<T> target, Object value) {
		return restoreObject(target, value, new TypeReference<T>() {
		});
	}

	@SuppressWarnings("unchecked")
	public static <T, R> R restoreObject(Class<T> target, Object value, TypeReference<R> references) {
		AbstractConverterEditor<?> editor = getBasicConverter(target, null, references.getType());
		editor.restore(value);
		return (R)editor.getValue();
	}
	
	public static <T> AbstractConverterEditor<?> getBasicConverter(Class<?> target) {
		return getBasicConverter(target,target,new TypeReference<Object>() {}.getType());
	}

	public static <T> AbstractConverterEditor<?> getBasicConverter(Class<?> target, Type genericType, Type type) {
		Class<? extends AbstractConverterEditor<?>> prototype = PropertyConverterEditor.getConverterClasses(target);
		if (prototype == null) {
			if (List.class.isAssignableFrom(target)) {
				prototype=ListConverterEditor.class;
			}else if(Set.class.isAssignableFrom(target)) {
				prototype=SetConverterEditor.class;
			}else if(Map.class.isAssignableFrom(target)) {
				prototype=MapConverterEditor.class;
			} else {
				prototype=ObjectConverterEditorExt.class;
			}
		}
		AbstractConverterEditor<?> editor = PropertyConverterEditor.newInstance(prototype, target);
		if (prototype.equals(ObjectConverterEditorExt.class)) {
			((ObjectConverterEditorExt) editor).setGeneric(genericType, type);
		} else if (prototype.equals(SetConverterEditor.class)) {
			((SetConverterEditor) editor).setGeneric(genericType, type);
		} else if (prototype.equals(ListConverterEditor.class)) {
			((ListConverterEditor) editor).setGeneric(genericType, type);
		}else if(prototype.equals(MapConverterEditor.class)) {
			((MapConverterEditor)editor).setGeneric(genericType,type);
		}
		return editor;
	}

	public static AbstractConverterEditor<?> getFieldConverterEditor(Field field, Class<?> valCls, Type typeReference) {
		// 获取字段上的转换器
		PropertyConverter convert = field.getAnnotation(PropertyConverter.class);
		Class<?> dataType = field.getType();
		// 获取数据类型上转换器
		if (convert == null) {
			convert = dataType.getAnnotation(PropertyConverter.class);
		}
		if (convert != null) {
			return PropertyConverterEditor.newInstance(convert.value(), dataType);
		}
		if(typeReference==null) {
			return ConverterEditorUtils.getBasicConverter(valCls, field.getGenericType(), null);
		}
		Type genericType = field.getGenericType();
		if (genericType instanceof TypeVariable) {
			// 泛型
			Map<String, Type> genericImplMap = getTypeReferenceMap(typeReference);
			Type t = genericImplMap.get(genericType.getTypeName());
			if(t!=null) {
				if (t instanceof ParameterizedType) {
					Class<?> ty=(Class<?>) ((ParameterizedType) t).getRawType();
					return ConverterEditorUtils.getBasicConverter(ty, genericType, typeReference);
				}else {
					return ConverterEditorUtils.getBasicConverter((Class<?>)t);
				}
			}
		}else {
			return ConverterEditorUtils.getBasicConverter(dataType, genericType, typeReference);
		}
		if(valCls!=null) {
			return ConverterEditorUtils.getBasicConverter(valCls);
		}
		return null;
	}

	public static AbstractConverterEditor<?> getGenericEditor(Type genericType,Type typeReference,int index) {
		if(genericType==null) {
			return null;
		}
		if(typeReference==null) {
			return null;
		}
		if (genericType instanceof TypeVariable) {
			//泛型
			Map<String,Type> genericImplMap=getTypeReferenceMap(typeReference);
			Type t=genericImplMap.get(genericType.getTypeName());
			if(t instanceof ParameterizedType) {
				Class<?> ty=(Class<?>)((ParameterizedType)t).getActualTypeArguments()[index];
				return ConverterEditorUtils.getBasicConverter(ty,genericType,typeReference);
			}else {
				//TODO:
			}
		}else if(genericType instanceof ParameterizedType) {
			Type type=((ParameterizedType) genericType).getActualTypeArguments()[index];
			if(type instanceof TypeVariable) {
				Map<String,Type> genericImplMap=ConverterEditorUtils.getTypeReferenceMap(typeReference);
				Type t=genericImplMap.get(type.getTypeName());
				return ConverterEditorUtils.getBasicConverter((Class<?>)t,genericType,typeReference);
			}else if(type instanceof ParameterizedType) {
				Class<?> ptype=(Class<?>)((ParameterizedType)type).getRawType();
				return ConverterEditorUtils.getBasicConverter(ptype,genericType,typeReference);
			}else {
				return ConverterEditorUtils.getBasicConverter((Class<?>)type,genericType,typeReference);
			}
		}
		return null;
	}
	
	public static Map<String, Type> getTypeReferenceMap(Type typeReference) {
		Map<String, Type> genericImplMap = new HashMap<>(100);
		if (typeReference instanceof ParameterizedType) {
			ParameterizedType typeImpl = (ParameterizedType) typeReference;
			TypeVariable<?>[] typeParameters = ((Class<?>) typeImpl.getRawType()).getTypeParameters();
			for (int i = 0; i < typeParameters.length; i++) {
				TypeVariable<?> tv = typeParameters[i];
				genericImplMap.put(tv.getName(), typeImpl.getActualTypeArguments()[i]);
			}
		}
		return genericImplMap;
	}

}
